// WorkHistory.cpp: implementation of the CWorkHistory class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Vincent.h"
#include "WorkHistory.h"
#include <algorithm>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CWorkHistory::CWorkHistory()
{
	m_nHistorySize = 0;				// 벡터가 현재 가리키는 곳을 0으로 한다.
	m_bkColor = RGB(0xff,0xff,0xff);// 맨 처음엔 흰색. (나중엔 새 문서 만들때 설정한다.)
	m_pbmpOld = NULL;				// BMP를 NULL로 설정
	m_bHasCache = false;			// 캐쉬가 없는 상태
	
	m_History.reserve(VECTOR_SIZE_RESERVED);	// 메모리를 예약해둔다.
}

CWorkHistory::~CWorkHistory()
{
	// BMP를 되돌린다. CDC와 CBitmap은 소멸자가 호출되기 때문에 메모리가 새지 않는다.
	if (m_pbmpOld)
		m_CacheDC.SelectObject(m_pbmpOld);
	
	DeleteAllNodes();	// 히스토리를 비운다. (각 노드가 가리키는 주소를 delete한다.)
}

bool CWorkHistory::IsUndoPossible()
{
	// m_nHistorySize가 0보다 크면 Undo가 가능하다.
	return ( 0 < m_nHistorySize );
}

bool CWorkHistory::IsRedoPossible()
{
	// m_nHistorySize가 size보다 작으면 Redo 가능하다.
	return ( m_nHistorySize < m_History.size() );
}

bool CWorkHistory::Undo()
{
	if (!IsUndoPossible())	// Undo가 불가능하면 false를 리턴.
		return false;
	
	m_nHistorySize--;		// 히스토리의 논리적 사이즈를 줄인다.
	RedrawCache();			// 캐쉬를 처음부터 다시 그린다.
	
	return true;
}

bool CWorkHistory::Redo()
{
	if (!IsRedoPossible())	// Redo가 불가능하면 false를 리턴.
		return false;
	
	m_nHistorySize++;		// 히스토리의 논리적 사이즈를 늘린다.
	m_History[m_nHistorySize-1]->Draw(&m_CacheDC);	// 캐쉬에 다시 하나 더 그린다.
	
	return true;
}

void CWorkHistory::AddNode(CDrawingNode *pNode)
{
	// Redo가 가능한지 판단해서, 가능한 상태에서 노드가 추가된 상황이면,
	// 현재 히스토리의 논리적 사이즈의 뒷부분은 모두 지운다.
	if (IsRedoPossible())
	{
		// 논리적 사이즈 부분의 다음칸부터 히스토리를 순회하면서 할당된 메모리를 해제하고,
		int size = (int)m_History.size();
		for (int i = m_nHistorySize; i < size; i++)
			delete m_History[i];
		
		// 벡터 자체를 m_nHistorySize칸으로 줄인다.
		m_History.resize(m_nHistorySize);
		
		m_nHistorySize = (int)m_History.size();	// 사이즈를 새로 셋팅.
		
		RedrawCache();	// 캐쉬를 처음부터 다시 그린다.
	}
	
	m_History.push_back(pNode);	// 히스토리의 끝부분에 새 노드를 삽입한다.
	m_nHistorySize++;			// 히스토리의 논리적 사이즈를 한개 늘린다.
	
	pNode->Draw(&m_CacheDC);	// 방금 삽입한 마지막 노드를 캐쉬에 그린다.
}

void CWorkHistory::Draw(CDC *pDC)
{
	// 캐쉬가 없으면, 초기화
	if (!m_bHasCache)
		InitCache(pDC);
	
	// 캐쉬를 pDC에 그려준다!!
	pDC->BitBlt(0, 0, m_sizeImage.cx, m_sizeImage.cy, &m_CacheDC, 0, 0, SRCCOPY);
}

void CWorkHistory::InitCache(CDC* pDC)
{
	// 이미 들어있는 값이 있으면, 캐쉬를 새로 설정한다.
	if (m_pbmpOld)
	{
		m_CacheDC.SelectObject(m_pbmpOld);
		m_CacheDC.DeleteDC();
		m_bmpCache.DeleteObject();
	}
	
	// 화면 DC와 호환성 있는 메모리 DC를 만든다.
	m_CacheDC.CreateCompatibleDC(pDC);
	
	// 화면 DC와 호환성 있는 메모리 비트맵을 만든다.
	m_bmpCache.CreateCompatibleBitmap(pDC, m_sizeImage.cx, m_sizeImage.cy);
	
	// m_CacheDC를 m_bmpCache와 연결한다.
	m_pbmpOld = (CBitmap*)m_CacheDC.SelectObject(&m_bmpCache);
	
	// m_CacheDC를 사이즈만큼 배경색으로 칠한다.
	m_CacheDC.FillSolidRect(0, 0, m_sizeImage.cx, m_sizeImage.cy, m_bkColor);
	
	// 캐쉬가 있음을 표시
	m_bHasCache = true;
}

void CWorkHistory::Initialize(COLORREF bkColor, CSize size)
{
	DeleteAllNodes();		// 히스토리의 모든 메모리를 delete하고, 히스토리 자체의 내용을 비운다.
	m_bkColor = bkColor;	// 배경색을 새로 설정한다.
	m_sizeImage = size;		// 이미지 사이즈를 새로 설정한다.
	m_bHasCache = false;	// 캐쉬를 다시..
}

void CWorkHistory::DeleteAllNodes()
{
	m_nHistorySize = 0;	// 히스토리의 논리적 사이즈를 0으로 한다.
	
	if (m_History.empty())	// 히스토리에 아무것도 안들어있으면 리턴한다.
		return;
	
	// 히스토리의 노드가 가리키는 메모리를 delete 한다.
	for (vector<CDrawingNode*>::iterator iter = m_History.begin(); iter != m_History.end(); ++iter)
		delete *iter;	// *iter 자체가 포인터이므로, 이 노드의 내용을 delete한다.
	
	// 히스토리 자체를 비운다.
	m_History.clear();
	
	// 메모리를 예약해둔다.
	m_History.reserve(VECTOR_SIZE_RESERVED);
}

void CWorkHistory::RedrawCache()
{
	// 캐쉬를 처음부터 다시 그려준다.
	
	//일단, 배경색으로 다시 칠하고,
	m_CacheDC.FillSolidRect(0, 0, m_sizeImage.cx, m_sizeImage.cy, m_bkColor);
	
	// 각 노드마다 Draw() 메소드를 호출한다.
	for (int i = 0; i < m_nHistorySize; i++)
		m_History[i]->Draw(&m_CacheDC);
}

CDC* CWorkHistory::GetCacheDC()
{
	return &m_CacheDC;
}

